﻿using System;
using System.Collections.Generic;
using System.Linq;
using TechTalk.SpecFlow;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WatiN.Core;

namespace Sketch.Test.Acceptance
{
    [Binding]
    class Validation
    {                
        private class ColumnInfo
        {
            public int Index { get; set; }
            public string Name { get; set; }

            public override string ToString()
            {
                return string.Format("{0} ({1})", Name, Index);
            }
        }

        [Then(@"I should see table with id ""(.*)""")]
        public void ValidateTableDataById(string tableId, TechTalk.SpecFlow.Table table)
        {            
            var htmlTable = WebBrowser.Current.Table(Find.ById(tableId));
            Assert.IsTrue(htmlTable.Exists, "The table {0} is not found", tableId);

            var actualTable = new Dictionary<ColumnInfo, List<string>>();
            var expectedTable = new Dictionary<ColumnInfo, List<string>>();
            
            //expected table
            var header = table.Header.ToList();
            for (var index = 0; index < header.Count; ++index)
            {
                expectedTable.Add(new ColumnInfo{Index = index, Name = header[index]}, new List<string>());    
            }

            foreach (var row in table.Rows)
            {
                foreach (var column in expectedTable)
                {
                    column.Value.Add(row[column.Key.Name]);
                }
            }

            //actual table
            foreach (var htmlRow in htmlTable.TableRows)
            {
                if (htmlRow.TableCells.Count == 0)
                {
                    // maybe it is header
                    var columns = htmlRow.ChildrenWithTag("TH");
                    for (var index = 0; index < columns.Count; ++index)
                    {
                        actualTable.Add(new ColumnInfo{Index = index, Name = columns[index].Text ?? ""}, new List<string>());
                    } 
                }
                else
                {
                    foreach (var column in actualTable)
                    {
                        var cell = htmlRow.TableCells[column.Key.Index];
                        var spans = cell.ChildrenWithTag("SPAN");
                        if (spans.Count > 0)
                            column.Value.Add(string.Join("#", spans.Select(x => x.Text)));                        
                        else
                            column.Value.Add(cell.Text ?? "");
                    }
                }
            }

            //assert
            foreach (var column in expectedTable)
            {
                var expectedColumn = column;

                var actualColumn = actualTable.SingleOrDefault(x => x.Key.Name.Trim().Equals(
                    expectedColumn.Key.Name.Trim(), StringComparison.OrdinalIgnoreCase));

                Assert.IsNotNull(actualColumn.Key, "The expected column '{0}' does not exist", expectedColumn.Key.Name);

                for (var index = 0; index < expectedColumn.Value.Count; index++)
                {
                    var expectedValue = expectedColumn.Value[index] ?? string.Empty;
                    var actualValue = index < actualColumn.Value.Count ? actualColumn.Value[index] ?? string.Empty : string.Empty;

                    Assert.AreEqual(expectedValue.Trim(), actualValue.Trim(), ignoreCase: true, 
                        message: "Incorrect value in column '{0}' row '{1}'", parameters: new object[] {expectedColumn.Key.Name, index + 1});
                }
            }
        }

        [Then(@"I should see the link with value ""(.*)""")]
        public void ValidateLinkExists(string linkValue)
        {
            var link = WebBrowser.Current.Link(Find.ByText(linkValue));
            Assert.IsTrue(link.Exists, string.Format("link with value '{0}' not found", linkValue));
        }

        [Then(@"I should see the warning message ""(.*)""")]
        public void ValidateWarningMessage(string message)
        {
            var span = WebBrowser.Current.Span(Find.ByClass("warning"));
            Assert.IsTrue(span.Exists, "The span with warning is not found");
            Assert.AreEqual(message, span.Text, ignoreCase: true, message: "The expected warning is not found");
        }
        [Then(@"I should see the error message ""(.*)""")]
        public void ValidateErrorMessage(string message)
        {
            var spans = WebBrowser.Current.Spans.Where(x => x.ClassName == "error" || x.ClassName == "field-validation-error");
            var errorFound = spans.Any(span => span.Text.Equals(message, StringComparison.OrdinalIgnoreCase));            
            Assert.IsTrue(errorFound, "The expected error {0} is not found", message);
        }

        [Then(@"I should see the text field with label ""(.*)""")]
        public void ValidateTextBox(string label)
        {
            var input = WebBrowser.Current.TextField(Find.ByLabelText(label));
            Assert.IsTrue(input.Exists, "The text field with label {0} not found", label);
        }

        [Then(@"I should see the button with label ""(.*)""")]
        public void ValidateButton(string label)
        {
            var input = WebBrowser.Current.Button(Find.ByValue(label));
            Assert.IsTrue(input.Exists, "The button with label {0} not found", label);
        }

        [Then(@"I should see the following notification ""(.*)""")]   
        public void ValidateNotification(string subject)
        {
            var constraint = Find.ByClass("notification").And(Find.ByText(subject));
            var span = WebBrowser.Current.Span(constraint);
            Assert.IsTrue(span.Exists, "The notification with subject '{0}' does not exist", subject);
        }

        [Then(@"I should see list with id ""(.*)""")]
        public void ValidateListDataById(string listId, TechTalk.SpecFlow.Table table)
        {
            var list = WebBrowser.Current.List(Find.ById(listId));

            var actual = string.Join(" |", list.ListItems.Select(e => e.Text));
            var expected = string.Join(" |", table.Rows.Select(r => r[0]));

            Assert.AreEqual(expected, actual, "List data is incorrect");
        }
    }
}
